package com.shiwaixiangcun.authz.config;

import com.shiwaixiangcun.authz.shiro.filter.AccessTokenContextFilter;
import com.shiwaixiangcun.authz.shiro.filter.OAuth2AuthenticationFilter;
import com.shiwaixiangcun.authz.shiro.realm.AccountRealm;
import com.shiwaixiangcun.authz.shiro.realm.DynamicPasswordAccountRealm;
import com.shiwaixiangcun.authz.shiro.realm.OAuth2Realm;
import com.shiwaixiangcun.authz.shiro.strategy.MkAtLeastOneSuccessfulStrategy;
import com.shiwaixiangcun.core.domain.DataSource;
import com.shiwaixiangcun.core.repository.support.MkAuditorAware;
import com.shiwaixiangcun.core.shiro.cache.ShiroCacheManager;
import com.shiwaixiangcun.core.shiro.session.mgt.MkSessionFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.AuditorAware;

import javax.servlet.Filter;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by SilentWu on 2017/2/27.
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) //启用自动代理功能。
public class SecurityConfig implements EnvironmentAware {

    private Environment env;

    @Bean
    public AccountRealm accountRealm() {
        return new AccountRealm();
    }

    @Bean
    public DynamicPasswordAccountRealm dynamicPasswordAccountRealm() {
        return new DynamicPasswordAccountRealm();
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    /**********************
     * 配置session
     *******************************/

    @Bean(name = "shiroCacheManager")
    public org.apache.shiro.cache.CacheManager cacheManager(org.springframework.cache.CacheManager cacheManager) {
        ShiroCacheManager shiroCacheManager = new ShiroCacheManager();
        shiroCacheManager.setSpringCacheManager(cacheManager);
        return shiroCacheManager;
    }

    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean(name = "sessionIdCookie")
    public Cookie sessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(env.getRequiredProperty("shiro.uid.cookie.name"));
        simpleCookie.setHttpOnly(Boolean.valueOf(env.getRequiredProperty("shiro.uid.cookie.httpOnly")));
        simpleCookie.setMaxAge(Integer.valueOf(env.getRequiredProperty("shiro.uid.cookie.maxAge")));
        simpleCookie.setDomain(env.getRequiredProperty("shiro.uid.cookie.domain"));
        simpleCookie.setPath(env.getRequiredProperty("shiro.uid.cookie.path"));
        return simpleCookie;
    }

    @Bean
    public SessionDAO sessionDAO(@Qualifier("shiroCacheManager") CacheManager cacheManager, SessionIdGenerator sessionIdGenerator) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setCacheManager(cacheManager);
        sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        return sessionDAO;
    }


    @Bean
    public SessionManager sessionManager(
            @Qualifier("shiroCacheManager") CacheManager cacheManager,
            @Qualifier("sessionIdCookie") Cookie sessionIdCookie,
            SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(Long.valueOf(env.getRequiredProperty("shiro.session.globalSessionTimeout")));
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        sessionManager.setCacheManager(cacheManager);
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionFactory(new MkSessionFactory());
        return sessionManager;
    }

    @Bean
    public OAuth2Realm oAuth2Realm() {
        OAuth2Realm oAuth2Realm = new OAuth2Realm();
        oAuth2Realm.setAuthorizationCachingEnabled(false);
        oAuth2Realm.setAuthorizationCachingEnabled(false);
        return oAuth2Realm;
    }

    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager(AccountRealm accountRealm,OAuth2Realm oAuth2Realm, DynamicPasswordAccountRealm dynamicPasswordAccountRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置自己的strategy
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); // 经检查ModularRealmAuthenticator是默认的authenticator 此处new没有问题
        authenticator.setAuthenticationStrategy(new MkAtLeastOneSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);

        securityManager.setRealms(Arrays.asList(accountRealm, dynamicPasswordAccountRealm, oAuth2Realm)); // oAuth2Realm必须在最后一个
        securityManager.setSessionManager(sessionManager);
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.env = environment;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl(env.getProperty("shiro.login.url"));
        factoryBean.setFilters(filterMap());
        factoryBean.setFilterChainDefinitionMap(filterChainMap());
        return factoryBean;
    }

    private Map<String, Filter> filterMap() {
        Map<String, Filter> filters =  new LinkedHashMap<>();
        filters.put("accessTokenContext", accessTokenContextFilter());
        filters.put("staffAppOauth2Authc", staffAPPOAuth2AuthenticationFilter());
        filters.put("customerAppOauth2Authc", customerAPPOAuth2AuthenticationFilter());
        return filters;
    }

    @Bean
    public Filter staffAPPOAuth2AuthenticationFilter() {
        OAuth2AuthenticationFilter oAuth2AuthenticationFilter = new OAuth2AuthenticationFilter();
        oAuth2AuthenticationFilter.setResourceId(DataSource.PROPERTY_APP.getScope());
        return oAuth2AuthenticationFilter;
    }

    @Bean
    public Filter customerAPPOAuth2AuthenticationFilter() {
        OAuth2AuthenticationFilter oAuth2AuthenticationFilter = new OAuth2AuthenticationFilter();
        oAuth2AuthenticationFilter.setResourceId(DataSource.PROPERTY_CUSTOMER_APP.getScope());
        return oAuth2AuthenticationFilter;
    }

    @Bean
    public Filter accessTokenContextFilter() {
        return new AccessTokenContextFilter();
    }

    private Map<String, String> filterChainMap() {
        Map<String, String> filters =  new LinkedHashMap<>();
        filters.put("/resources/**", "anon");
        filters.put("/favicon.png", "anon");
        filters.put("/", "anon");
        filters.put("/mi/**", "anon");   // mobile/ignore   =>  mi
        filters.put("/oauth2/**", "anon");
        filters.put("/mc/**", "accessTokenContext,customerAppOauth2Authc");   //        /mc/** => mobile/customer
        filters.put("/ms/**", "accessTokenContext,staffAppOauth2Authc");
        filters.put("/**", "authc");
        return filters;
    }

    @Bean
    public AuditorAware<Long> auditorAware() {
        return new MkAuditorAware();
    }

}
